//#include "sncneighbor.hh"
#include "matrix.hh"
#include "snc.hh"

//#include "sncpacket.hh"

//#include "codedblock.hh"
//#include "sncneighbor.hh"


#include <click/glue.hh>
#include <click/confparse.hh>
#include <click/error.hh>
#include <clicknet/ether.h>
#include <clicknet/llc.h>

//CLICK_DECLS

SNC::SNC():_max(0),
		_poolcap(1000),
		_sendqcap(1000),
		_pkt_threshold(500),
		_retry(2),
		_period(500),
		_pktpool(_poolcap),
		_sendq(_sendqcap),
		_sncencapedq(500),
		_neighbor_manag(this),
		_timer(this),
		_router(false){
	DPrintf("SNC constructed!");
}

int SNC::configure(Vector<String> & conf, ErrorHandler *errh) {

	DPrintf("size=%d",conf.size());
	for(int i=0;i<conf.size();i++){
		DPrintf("%d %s\n",i,conf[i].c_str());
	}
	DPrintf("SNC configuring!!");
    int ret = cp_va_kparse(conf, this, errh,
    		"ETH", 0,cpEtherAddress, &_eth,
    		"MAX", 0, cpUnsigned, &_max,
    		"SENDQ", 0, cpUnsigned, &_sendqcap,
    		"POOL" , 0, cpUnsigned, &_poolcap,
    		"THRESHOLD", 0, cpUnsigned, &_pkt_threshold,
    		"RETRY", 0, cpUnsigned, &_retry,
    		"PERIOD", 0, cpUnsigned, &_period,
    		"ROUTER", 0, cpBool, &_router,
    		cpEnd);
    if(ret<0) return -1;
    DPrintf("SNC configured!!");
    _sendq.set_capcity(_sendqcap);
    _pktpool.set_cap(_poolcap);
    _neighbor_manag.set_poolcap(_poolcap);
    _neighbor_manag.set_threshold(_pkt_threshold);
    DPrintf("SNC reseted!!");
    return ret;
}

int SNC::initialize (ErrorHandler *){
	_timer.initialize(this);
	_timer.schedule_after_msec(_period);
	return 0;
}

void SNC::run_timer(Timer *){
	check_padding();
	_timer.schedule_after_msec(_period);
}

//be careful
bool SNC::get_sncheader_info(Packet* p,uint8_t& type,
		 encoded_header** encoded_data,uint8_t& num1,uint8_t** code_vector,
		 report_header** report_data,uint8_t& num2,
		 ack_header** ack_data,uint8_t& num3){
	type=*(uint8_t*)p->data();
	if(!(type & (SNC_NO_DATA | SNC_DATA_NORMAL | SNC_DATA_ENCODED))) return false;
	//get encoded_header
	p->pull(sizeof(uint8_t));
	num1=*(uint8_t*)p->data();
	p->pull(sizeof(uint8_t));
	if(num1!=0){
		*code_vector=new uint8_t[num1];
		memset(code_vector,1,num1);
		//memcpy(*code_vector,(uint8_t*)p->data(),num1);
		//p->pull(num1);
		*encoded_data=new  encoded_header[num1];
		memcpy((uint8_t*)*encoded_data,(uint8_t*)p->data(),sizeof(encoded_header)*num1);
		p->pull(sizeof(encoded_header)*num1);
	}
	//get report_header
	num2=*(uint8_t*)p->data();
	p->pull(sizeof(uint8_t));
	if(num2!=0){
		*report_data=new report_header[num2];
		memcpy((uint8_t*)*report_data,(uint8_t*)p->data(),sizeof(report_header)*num2);
		p->pull(sizeof(report_header)*num2);
	}
	//get ack_header
	num3=*(uint8_t*)p->data();
	p->pull(sizeof(uint8_t));
	if(num3!=0){
		//pkt_seq=*(uint16_t*)p->data();
		//p->pull(sizeof(uint16_t));
		*ack_data=new ack_header[num3];
		memcpy((uint8_t*)*ack_data,(uint8_t*)p->data(),sizeof(ack_header)*num3);
		p->pull(sizeof(ack_header)*num3);
	}
	return true;
}

bool SNC::check_next_hop(encoded_header* encoded,uint8_t num){
	for(uint8_t i=0;i<num;i++){
		if(memcpy(_eth.data(),&encoded[i]._next_hop,6)==0) return true;
	}
	return false;
}

bool SNC::check_need_decode(encoded_header* encoded,uint8_t num){
	unsigned count=0;
	for(unsigned i=0;i<num;i++){
		NativePkt* pkt=_pktpool.get(encoded[i]._pkt_id);
		if(pkt) count++;
	}
	if(count!=num-1) return false;
	else return true;
}

//decode must call after check_need_decode
int32_t SNC::decode(encoded_header* encoded,uint8_t* code_vector,uint8_t num,uint8_t* data,uint16_t size){
	FullGEMatrix matrix(num);

	CodedBlock* block=new CodedBlock[num]();
	int unknow;
	for(unsigned i=0;i<num;i++){
		NativePkt* native=_pktpool.get(encoded[i]._pkt_id);
		if(!native){
			unknow=i;
			continue;
		}
		block[i].set(0,0,native->_data,native->_size);
		block[i].make_coeff(num,i);
		matrix.add(&block[i]);
	}
	block[unknow].set(code_vector,num,data,size);
	bool r=matrix.add(&block[unknow]);
	if(r){
		const uint8_t* encodeddata=block[unknow].get_data();
		const uint8_t* actualdata=encodeddata+sizeof(uint16_t);
		uint16_t actualsize=*(uint16_t*)encodeddata;
		memcpy(data,encodeddata,actualsize);
		return actualsize;
	}
	else return -1;
}

void SNC::update_pktpool(uint32_t src_ip,uint16_t seqno,const uint8_t* data,uint32_t size){
	NativePkt* pkt=new NativePkt;
	uint8_t* d=new uint8_t[size];
	memcpy(d,data,size);
	pkt->_data=d;
	pkt->_size=size;
	uint32_t pkt_id=MAKE_PKT_ID(src_ip,seqno);
	_pktpool.store(pkt_id,pkt);
	pad_report(src_ip,seqno);
}

void SNC::pad_report(const uint32_t src_ip,uint16_t seqno){
	Vector<report >::iterator iter;
	for(iter=_padding_report.begin();iter!=_padding_report.end();++iter){
		if((*iter)._src_ip==src_ip){
			if((*iter)._seqno.empty()){
				(*iter)._seqno.push_back(seqno);
			}
			else{
				SNCoding::List<uint16_t >::iterator i;
				for(i=(*iter)._seqno.begin();i!=(*iter)._seqno.end();++i){
					if(seqno>=(*i)){
						(*iter)._seqno.insert(i,seqno);
						return;
					}
				}
				(*iter)._seqno.push_back(seqno);
				return;
			}
		}
	}
	//can not find,we have to create
	report tem;
	tem._src_ip=src_ip;
	_padding_report.push_back(tem);
	_padding_report.back()._seqno.push_back(seqno);
}

void SNC::pad_ack(const uint8_t* mac,uint16_t seqno){

	Vector<ack >::iterator iter;
	for(iter=_padding_ack.begin();iter!=_padding_ack.end();++iter){
		if(memcmp((*iter)._dst,mac,6)==0){
			if((*iter)._seqno.empty()){
				(*iter)._seqno.push_back(seqno);
			}
			else{
				SNCoding::List<uint16_t >::iterator i;
				for(i=(*iter)._seqno.begin();i!=(*iter)._seqno.end();++i){
					if(seqno>=(*i)){
						(*iter)._seqno.insert(i,seqno);
						return;
					}
				}
				(*iter)._seqno.push_back(seqno);
				return;
			}
		}
	}
	ack tem;
	memcpy(&tem._dst,mac,6);
	_padding_ack.push_back(tem);
	_padding_ack.back()._seqno.push_back(seqno);
}

 uint32_t SNC::make_encode_header(Natives& natives,uint8_t* data){
	//struct click_wifi w;
	//EtherAddress bssid;
	//EtherAddress src;
	//EtherAddress dst;
	uint32_t size=1+1;//1 for TYPE 1 for ENCODED_NUM
	//if(!natives.empty()){
	//	encoded_header_size+=sizeof(encoded_header)*natives.size();
	//}
	//*encoded=new uint8_t[encoded_header_size];
	switch (natives.size()){
		case 0: {
			*data=SNC_NO_DATA;
			*(data+1)=0;
		}break;
		case 1: {
			*data=SNC_DATA_NORMAL;
			*(data+1)=0;
			//encoded_header* header=(encoded_header*)(data+2);
			//Packet* p=natives.front();
			//get_wifi_info(p,bssid,src,dst,&w);
			//header->_pkt_id=MAKE_PKT_ID(p->ip_header()->ip_src.s_addr,p->ip_header()->ip_id);
			//header->_next_hop=dst.data();
			//size+=sizeof(encoded_header);
		}break;
		default: {
			*data=SNC_DATA_ENCODED;
			*(data+1)=natives.size();
			encoded_header* header=(encoded_header*)(data+2);
			//do not include the first packet!!!!
			for(int i=0;i<natives.size();i++){
				Packet* p=natives[i]; //ether packet
				//get_wifi_info(p,bssid,src,dst,&w);
				header[i-1]._pkt_id=MAKE_PKT_ID(p->ip_header()->ip_src.s_addr,p->ip_header()->ip_id);
				//header[i-1]._next_hop=p->ether_header()->ether_dhost;
				memcpy(&header[i-1]._next_hop,&p->ether_header()->ether_dhost,6);
			}
			size+=sizeof(encoded_header)*(natives.size());
		}break;
	}
	return size;
}

uint32_t SNC::make_report_header(uint8_t* data){
	uint32_t size=1;
	*data=_padding_report.size();
	report_header* header=(report_header*)(data+1);
	for(int i=0;i<_padding_report.size();++i){
		header[i]._src_ip=_padding_report[i]._src_ip;
		header[i]._bit_map=0;
		SNCoding::List<uint16_t >::iterator iter=_padding_report[i]._seqno.begin();
		uint16_t last_pkt=(*iter);
		header[i]._last_pkt=last_pkt;
		iter++;
		while(iter!=_padding_report[i]._seqno.end()){
			bitmap_sizetype offset=1;
			offset<<=(last_pkt-(*iter)-1);
			if(!offset) break;
			header[i]._bit_map |=offset;
			iter++;
		}
		size+=sizeof(report_header);
	}
	return size;
}

uint32_t SNC::make_ack_header(uint8_t* data){
	uint32_t size=1;
	*data=_padding_ack.size();
	ack_header* header=(ack_header*)(data+1);
	for(int i=0;i<_padding_ack.size();++i){
		//header[i]._neighbor=_padding_ack[i]._dst;
		memcpy(&header[i]._neighbor,&_padding_ack[i]._dst,6);
		header[i]._bit_map=0;
		SNCoding::List<uint16_t >::iterator iter=_padding_ack[i]._seqno.begin();
		uint16_t last_pkt=(*iter);
		header[i]._last_ack=last_pkt;
		iter++;
		while(iter!=_padding_ack[i]._seqno.end()){
			bitmap_sizetype offset=1;
			offset<<=(last_pkt-(*iter)-1);
			if(!offset) break;
			header[i]._bit_map |=offset;
			iter++;
		}
		size+=sizeof(ack_header);
	}
	return size;
}

uint32_t SNC::calc_DATA_size(Natives& natives){
	uint32_t max=0;
	for(int i=0;i<natives.size();i++){
		if(natives[i]->length()>=max)
			max=natives[i]->length();
	}
	//add 2 bytes for storing the data length
	return max+2-sizeof(click_ether);
}

uint32_t SNC::calc_sncheader_size(Natives& natives){
	//uint32_t size=1;
	uint32_t encoded_header_size=1;
	if(natives.size()>1){
		//do not include the first packet
		encoded_header_size+=sizeof(encoded_header)*(natives.size());
	}
	uint32_t report_header_size=1;
	//List<report >::iterator i1;
	//uint32_t count=0; //the number of bitmap
	//for(i1=_padding_report.begin();i1!=_padding_report.end();++i1){
	//	uint32_t max_seq=(*i1)._seqno.front();
	//	uint32_t min_seq=(*i1)._seqno.back();
	//	uint32_t tem=(max-min)/(sizeof(bitmap_sizetype)*byte_size);
	//	count+=(max-mix)%(sizeof(bitmap_sizetype)*byte_size) ? tem+1 : tem ;
	//}
	report_header_size+=sizeof(report_header)*_padding_report.size();

	//count=0;
	uint32_t ack_header_size=1; //1 for ACK_NUM
	//List<ack >::iterator i2;
	//for(i2=__padding_ack.begin();i2!=_padding_ack.end();++i2){
	//	uint32_t max_seq=(*i2)._seqno.front();
	//	uint32_t min_seq=(*i2)._seqno.back();
	//	uint32_t tem=(max-min)/(sizeof(bitmap_sizetype)*byte_size);
	//	count+=(max-mix)%(sizeof(bitmap_sizetype)*byte_size) ? tem+1 : tem ;
	//}
	ack_header_size+=sizeof(ack_header)*_padding_ack.size();
	return 1+encoded_header_size+report_header_size+ack_header_size;


}

bool SNC::get_wifi_info(
		Packet* p,
		EtherAddress& bssid,
		EtherAddress& src,
		EtherAddress& dst,
		click_wifi& wifi){
	uint8_t dir;
	wifi = *(struct click_wifi *) p->data();
	int wifi_header_size = sizeof(struct click_wifi);
	if ((wifi.i_fc[1] & WIFI_FC1_DIR_MASK) == WIFI_FC1_DIR_DSTODS)
		wifi_header_size += WIFI_ADDR_LEN;
	if (WIFI_QOS_HAS_SEQ(&wifi))
		wifi_header_size += sizeof(uint16_t);

	if (p->length() < wifi_header_size + sizeof(struct click_llc)) {
		return false;
	}

	if (wifi.i_fc[1] & WIFI_FC1_WEP) {
		return false;
	}

	dir = wifi.i_fc[1] & WIFI_FC1_DIR_MASK;

	switch (dir) {
		case WIFI_FC1_DIR_NODS:
			dst = EtherAddress(wifi.i_addr1);
			src = EtherAddress(wifi.i_addr2);
			bssid = EtherAddress(wifi.i_addr3);
			break;
		case WIFI_FC1_DIR_TODS:
			bssid = EtherAddress(wifi.i_addr1);
			src = EtherAddress(wifi.i_addr2);
			dst = EtherAddress(wifi.i_addr3);
			break;
		case WIFI_FC1_DIR_FROMDS:
			dst = EtherAddress(wifi.i_addr1);
			bssid = EtherAddress(wifi.i_addr2);
			src = EtherAddress(wifi.i_addr3);
			break;
		case WIFI_FC1_DIR_DSTODS:
			dst = EtherAddress(wifi.i_addr1);
			src = EtherAddress(wifi.i_addr2);
			bssid = EtherAddress(wifi.i_addr3);
			break;
		default:
			click_chatter("%{element}: invalid dir %d\n",
							this,
							dir);
			dst = EtherAddress(wifi.i_addr1);
			src = EtherAddress(wifi.i_addr2);
			bssid = EtherAddress(wifi.i_addr3);
	}
	return true;
}

void SNC::push(int port, Packet *p) {
    if(p->shared()) {
        click_chatter("Warning: uniqueify in push() is expensive!\n");
    }
    WritablePacket *p_out = p->uniqueify();
    if(!p_out) return;


    switch(port) {
    //expect packet with wifi header
		case 0:{
			struct click_wifi w;
			EtherAddress bssid;
			EtherAddress src;
			EtherAddress dst;
			uint8_t type;
			uint8_t num1;
			encoded_header* encoded_data=0;
			uint8_t* code_vector=0;
			uint8_t num2;
			report_header* report_data=0;
			uint8_t num3;
			//uint16_t local_pkt_seq;
			ack_header* ack_data=0;
			if(!get_wifi_info(p_out,bssid,src,dst,w)){
				p_out->kill();
				return;
			}
			p_out->pull(sizeof(click_wifi));
			if(get_link_credits(p_out)){
				p_out->kill();
				return;
			}

			if(!get_sncheader_info(p_out,type,&encoded_data,num1,&code_vector,&report_data,num2,&ack_data,num3)){
				p_out->kill();
				return;
			}
			//deal with ack
			_neighbor_manag.update_ackqueue(src.data(),ack_data,num3);
			//deal with report
			_neighbor_manag.update_pktpool(src.data(),report_data,num2);
			//deal with encoded
			_neighbor_manag.update_pktpool(src.data(),encoded_data,num1);
			switch (type){
				case SNC_NO_DATA:{
					p_out->kill();
				}break;
				case SNC_DATA_NORMAL:{
					const click_ip *ip = reinterpret_cast<const click_ip *>(p_out->data());
					p_out=p_out->push(sizeof(click_wifi));
					*(struct click_wifi *) p_out->data()=w;
					uint32_t pkt_id=MAKE_PKT_ID(ip->ip_src.s_addr,ip->ip_id);
					//update the neighbor's pktpool who sent this pkt
					_neighbor_manag.update_pktpool(src.data(),pkt_id);
					//update self pktpool
					update_pktpool(ip->ip_src.s_addr,ip->ip_id,p_out->data(),p_out->length());
					if(check_next_hop(encoded_data,num1))
						pad_ack(src.data(),ip->ip_id);
					output(0).push(p_out);
				}break;
				case SNC_DATA_ENCODED:{
					if(check_need_decode(encoded_data,num1)){
						int32_t actualsize;
						if((actualsize=decode(encoded_data,code_vector,num1,p_out->data(),p_out->length()))!=-1){
							//strip 2 bytes for storing the actual data size
							p_out->pull(sizeof(uint16_t));
							//take off the padding data
							p_out->take(p_out->length()-actualsize);
							const click_ip *ip = reinterpret_cast<const click_ip *>(p_out->data());
							p_out=p_out->push(sizeof(click_wifi));
							*(struct click_wifi *) p_out->data()=w;
							uint32_t pkt_id=MAKE_PKT_ID(ip->ip_src.s_addr,ip->ip_id);
							//we do not need to store pkt in the neighbor's pktpool,
							//because we have done it at _neighbor_manag.update_pktpool
							//_neighbor_manag.update_pktpool(src.data(),pkt_id);
							update_pktpool(ip->ip_src.s_addr,ip->ip_id,p_out->data(),p_out->length());
							if(check_next_hop(encoded_data,num1))
								pad_ack(src.data(),ip->ip_id);
							output(0).push(p_out);
						}
					}

				}break;
			default:
				p_out->kill();
				click_chatter("Can not recognize the type %d\n",type);
			}
			delete[] encoded_data;
			delete[] code_vector;
			delete[] report_data;
			delete[] ack_data;
		}break;
		//expect ethter packet
		case 1:{
			//p_out->set_packet_type_anno(Packet::OUTGOING);
			if(!_neighbor_manag.add_to_sendq(p_out->ether_header()->ether_dhost,p_out)) p_out->kill();
			//if(!_sendq.push_back(sp)) p_out->kill();
			return;
		}break;
		//case 2:{
		//	p_out->set_packet_type_anno(Packet::FASTROUTE);
		//	if(!_neighbor_manag.add_to_sendq(p_out->ether_header()->ether_dhost,p_out)) p_out->kill();
			//if(!_sendq.push_back(sp)) p_out->kill();
		//	return;
		//}
		default:break;
    }


}

//expect a ether packet
WritablePacket * SNC::prepare_encoded_pkt(Packet* p){
	//struct click_wifi w;
	//EtherAddress bssid;
	//EtherAddress src;
	//EtherAddress dst;
	//if(!get_wifi_info(p,bssid,src,dst,&w)){
	//	p->kill();
	//	return 0;
	//}
	struct click_ether e;
	Natives natives;
	natives.push_back(p);
	if(p->packet_type_anno()==Packet::OTHERHOST) _neighbor_manag.codepoll(natives);
	//uint32_t header_size=32+sizeof(click_wifi)+calc_sncheader_size(natives)-sizeof(click_ether);
	uint32_t snc_header_size=calc_sncheader_size(natives);
	uint32_t header_size=sizeof(click_wifi)+snc_header_size;
	uint32_t data_size=calc_DATA_size(natives);
	WritablePacket * p_out=Packet::make(header_size,0,data_size,0);
	e=*(p->ether_header());
	//p->pull(sizeof(click_ether));
	//strip the ether
	//p_out->pull(sizeof(click_ether));
	//Natives::iterator iter;
	//encode pkts
	p->pull(sizeof(click_ether));
	CodedBlock coded(0,0,const_cast<uint8_t* >(p->data()),p->length(),data_size-p->length());
	p->push(sizeof(click_ether));
	for(int i=1;i<natives.size();i++){
		natives[i]->pull(sizeof(click_ether));
		CodedBlock tem(0,0,const_cast<uint8_t* >(natives[i]->data()),natives[i]->length(),data_size-natives[i]->length(),false);
		coded.add(tem,1);
		natives[i]->push(sizeof(click_ether));
	}
	memcpy(p_out->data(),coded.get_data(),coded.datasize());
	//p_out=p_out->push(sizeof(click_ether)+header_size);
	p_out=p_out->push(snc_header_size+sizeof(click_ether));
	*(click_ether*)p_out->data()=e;
	uint32_t len=sizeof(click_ether);
	len+=make_encode_header(natives,p_out->data()+len);
	len+=make_report_header(p_out->data()+len);
	len+=make_ack_header(p_out->data()+len);
	if(len!=snc_header_size+sizeof(click_ether)){
		click_chatter("prepare_encoded_pkt error!!");
		p_out->kill();
		return 0;
	}
	return p_out;
	//int encoded_pkt_num=

}

Packet *SNC::pull(int port){
	if(!_sncencapedq.empty()){
		Packet* p=_sncencapedq.front();
		_sncencapedq.pop_front();
		return p;
	}
	if(!_sendq.empty()){
		Packet* p=_sendq.front();
		WritablePacket * p_out=prepare_encoded_pkt(p);
		if(p_out){
			_retry++;
			if(_retry>MAXRETRY){
				_neighbor_manag.remove_from_sendq(p->ether_header()->ether_dhost,_sendq.begin());
				_retry=0;
			}
			return p_out;
		}
		else{
			_retry=0;
			_neighbor_manag.remove_from_sendq(p->ether_header()->ether_dhost,_sendq.begin());
			return 0;
		}
	}
	return 0;
}

void SNC::clear_pktpool(){
	//HashTable<uint32_t,NativePkt* >::
}

void SNC::check_padding(){
	if(_padding_ack.empty() && _padding_report.empty()) return;
	Natives natives;
	uint32_t header_size=calc_sncheader_size(natives);
	//32 for ATHDESC_HEADER_SIZE
	WritablePacket * p_out=Packet::make(32+sizeof(click_wifi)+header_size,0,0,0);
	//strip the wifi and snc header
	p_out=p_out->push(header_size);
	//clicl_wifi* wifiheader=(click_wifi*)p_out->data();
	uint32_t len=0;
	len+=make_encode_header(natives,p_out->data()+len);
	len+=make_report_header(p_out->data()+len);
	len+=make_ack_header(p_out->data()+len);
	if(len!=header_size){
		click_chatter("check_padding error!!");
		p_out->kill();
		return ;
	}
	p_out=p_out->push(sizeof(click_ether));
	click_ether* header=(click_ether*)p_out->data();
	memset(&header->ether_dhost,0xFF,6);
	//header->ether_shost=_eth.data();
	memcpy(&header->ether_shost,_eth.data(),6);
	header->ether_type=ETHERTYPE_IP;
	_sncencapedq.push_front(p_out);
}

enum {H_START, H_STOP};

int SNC::write_param(const String &in_s, Element *e, void *vparam,
                ErrorHandler *errh){
	switch (*(int*)vparam){
	case H_START:break;
	case H_STOP:break;
	default:break;
	}

	return 0;
}

void SNC::add_handlers() {
    add_write_handler("start", write_param, (void *)H_START);
    add_write_handler("stop", write_param, (void *)H_STOP);
}


//CLICK_ENDDECLS
EXPORT_ELEMENT(SNC)
